<script setup>
import { ref } from 'vue'

// 状态变量
const originalImage = ref(null)
const cropArea = ref(null)
const cropImage = ref(null)
const cropFrame = ref(null)
const resizeHandle = ref(null)
const resultCanvas = ref(null)
const fileInput = ref(null)

const cropFrameState = ref({
    x: 0,
    y: 0,
    size: 0,
    maxSize: 0,
    minSize: 50,
    imageWidth: 0,
    imageHeight: 0,
    imageTop: 0,
    imageLeft: 0,
})

const isDragging = ref(false)
const isResizing = ref(false)
const startX = ref(0)
const startY = ref(0)
const startSize = ref(0)
const startFrameX = ref(0)
const startFrameY = ref(0)
const selectedResolution = ref('512')
const isVisible = ref(false)
let resolvePromise = null

const openCropper = () => {
    fileInput.value?.click()
    return new Promise((resolve) => {
        resolvePromise = resolve
    })
}

const clearImageInput = () => {
    if (fileInput.value) fileInput.value.value = ''
}

defineExpose({
    openCropper,
    clearImageInput
})

const initCropArea = (img) => {
    if (!cropImage.value || !cropArea.value) return

    cropImage.value.src = img.src

    const maxWidth = window.innerWidth * 0.9
    const maxHeight = window.innerHeight * 0.9

    let displayWidth, displayHeight

    if (img.width / img.height > maxWidth / maxHeight) {
        displayWidth = maxWidth
        displayHeight = displayWidth * (img.height / img.width)
    } else {
        displayHeight = maxHeight
        displayWidth = displayHeight * (img.width / img.height)
    }

    cropArea.value.style.width = `${displayWidth}px`
    cropArea.value.style.height = `${displayHeight}px`

    cropFrameState.value.imageWidth = displayWidth
    cropFrameState.value.imageHeight = displayHeight
    cropFrameState.value.maxSize = Math.min(displayWidth, displayHeight)
    cropFrameState.value.size = cropFrameState.value.maxSize
    cropFrameState.value.x = (displayWidth - cropFrameState.value.size) / 2
    cropFrameState.value.y = (displayHeight - cropFrameState.value.size) / 2

    updateCropFrame()

    isVisible.value = true
}

const updateCropFrame = () => {
    if (!cropFrame.value) return

    cropFrame.value.style.left = `${cropFrameState.value.x}px`
    cropFrame.value.style.top = `${cropFrameState.value.y}px`
    cropFrame.value.style.width = `${cropFrameState.value.size}px`
    cropFrame.value.style.height = `${cropFrameState.value.size}px`
}

const getEventPosition = (e) => {
    if ('touches' in e && e.touches.length > 0) {
        return {
            x: e.touches[0].clientX,
            y: e.touches[0].clientY,
        }
    }
    return {
        x: e.clientX,
        y: e.clientY,
    }
}

const handleDragStart = (e) => {
    e.preventDefault()

    if (e.target === resizeHandle.value) return

    isDragging.value = true
    const pos = getEventPosition(e)
    startX.value = pos.x
    startY.value = pos.y
    startFrameX.value = cropFrameState.value.x
    startFrameY.value = cropFrameState.value.y

    document.addEventListener('mousemove', handleDragMove)
    document.addEventListener('mouseup', handleDragEnd)
    document.addEventListener('touchmove', handleDragMove)
    document.addEventListener('touchend', handleDragEnd)
}

const handleDragMove = (e) => {
    if (!isDragging.value) return
    e.preventDefault()

    const pos = getEventPosition(e)
    let newX = startFrameX.value + (pos.x - startX.value)
    let newY = startFrameY.value + (pos.y - startY.value)

    newX = Math.max(0, Math.min(newX, cropFrameState.value.imageWidth - cropFrameState.value.size))
    newY = Math.max(0, Math.min(newY, cropFrameState.value.imageHeight - cropFrameState.value.size))

    cropFrameState.value.x = newX
    cropFrameState.value.y = newY

    updateCropFrame()
}

const handleDragEnd = () => {
    isDragging.value = false
    document.removeEventListener('mousemove', handleDragMove)
    document.removeEventListener('mouseup', handleDragEnd)
    document.removeEventListener('touchmove', handleDragMove)
    document.removeEventListener('touchend', handleDragEnd)
}

const handleResizeStart = (e) => {
    e.preventDefault()
    e.stopPropagation()

    isResizing.value = true
    const pos = getEventPosition(e)
    startX.value = pos.x
    startY.value = pos.y
    startSize.value = cropFrameState.value.size

    document.addEventListener('mousemove', handleResizeMove)
    document.addEventListener('mouseup', handleResizeEnd)
    document.addEventListener('touchmove', handleResizeMove)
    document.addEventListener('touchend', handleResizeEnd)
}

const handleResizeMove = (e) => {
    if (!isResizing.value) return
    e.preventDefault()

    const pos = getEventPosition(e)
    const deltaX = pos.x - startX.value
    const deltaY = pos.y - startY.value

    const delta = Math.max(deltaX, deltaY)
    let newSize = startSize.value + delta

    newSize = Math.max(cropFrameState.value.minSize, newSize)
    newSize = Math.min(newSize, cropFrameState.value.imageWidth - cropFrameState.value.x)
    newSize = Math.min(newSize, cropFrameState.value.imageHeight - cropFrameState.value.y)

    cropFrameState.value.size = newSize

    updateCropFrame()
}

const handleResizeEnd = () => {
    isResizing.value = false
    document.removeEventListener('mousemove', handleResizeMove)
    document.removeEventListener('mouseup', handleResizeEnd)
    document.removeEventListener('touchmove', handleResizeMove)
    document.removeEventListener('touchend', handleResizeEnd)
}

const handleFileSelect = (e) => {
    const input = e.target
    const file = input.files?.[0]

    if (file && file.type.startsWith('image/')) {
        const reader = new FileReader()
        reader.onload = (event) => {
            originalImage.value = new Image()
            originalImage.value.onload = () => {
                if (originalImage.value) initCropArea(originalImage.value)
            }
            originalImage.value.src = event.target?.result
        }
        reader.readAsDataURL(file)
    }
}

const confirmCrop = async () => {
    if (!originalImage.value || !resultCanvas.value) return

    const tempCanvas = document.createElement('canvas')
    const tempContext = tempCanvas.getContext('2d')

    if (!tempContext) return

    const sourceX =
        (cropFrameState.value.x / cropFrameState.value.imageWidth) * originalImage.value.width
    const sourceY =
        (cropFrameState.value.y / cropFrameState.value.imageHeight) * originalImage.value.height
    const sourceSize =
        (cropFrameState.value.size / cropFrameState.value.imageWidth) * originalImage.value.width

    const finalSize =
        selectedResolution.value === 'original' ? sourceSize : parseInt(selectedResolution.value)

    tempCanvas.width = finalSize
    tempCanvas.height = finalSize

    tempContext.drawImage(
        originalImage.value,
        sourceX,
        sourceY,
        sourceSize,
        sourceSize,
        0,
        0,
        finalSize,
        finalSize,
    )

    const blob = await new Promise((resolve) => {
        tempCanvas.toBlob((blob) => {
            resolve(blob)
        }, 'image/png')
    })

    isVisible.value = false
    resolvePromise?.(blob)
}

const cancelCrop = () => {
    clearImageInput()
    isVisible.value = false
    resolvePromise?.(null)
}
</script>
<template>
    <div v-show="isVisible" class="crop-container">
        <div class="crop-area" ref="cropArea">
            <img ref="cropImage" class="crop-image" draggable="false" alt="待裁剪图片"  src=""/>
            <!-- 裁切框 -->
            <div ref="cropFrame" class="crop-frame" @mousedown="handleDragStart" @touchstart="handleDragStart">
                <!-- 可以放大的角  -->
                <div ref="resizeHandle" class="crop-resize-handle" @mousedown="handleResizeStart"
                     @touchstart="handleResizeStart"></div>
            </div>
        </div>
        <div class="crop-controls">
            <div>
                <button class="crop-btn crop-confirm" @click="confirmCrop" style="margin-right: 5px">确认</button>
                <button class="crop-btn crop-cancel" @click="cancelCrop" style="margin-left: 5px">取消</button>
            </div>
        </div>
        <input type="file" ref="fileInput" @change="handleFileSelect" accept="image/*" style="display: none" />
        <canvas ref="resultCanvas" style="display: none"></canvas>
    </div>
</template>

<style scoped>
.crop-container {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.8);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 999;
    overflow: hidden;
    touch-action: none;
}

.crop-area {
    position: relative;
    overflow: hidden;
    max-width: 90vw;
    max-height: 90vh;
}

.crop-image {
    display: block;
    max-width: 100%;
    max-height: 100%;
    touch-action: none;
}

.crop-frame {
    position: absolute;
    border: 2px solid white;
    box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.7);
    cursor: move;
    touch-action: none;
}

.crop-resize-handle {
    position: absolute;
    width: 24px;
    height: 24px;
    right: -12px;
    bottom: -12px;
    cursor: nwse-resize;
    background-color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    touch-action: none;
}


.crop-controls {
    position: absolute;
    bottom: 100px;
    left: 50%;
    transform: translateX(-50%);
    text-align: center;
    display: inline-block;
}

.resolution-options {
    display: flex;
    gap: 10px;
    margin-bottom: 10px;
}

.resolution-option {
    background-color: white;
    color: white;
    padding: 8px 15px;
    border-radius: 4px;
    cursor: pointer;
    border: none;
    font-size: 14px;
}

.resolution-option.selected {
    background-color: white;
    box-shadow: 0 0 0 2px white;
}

.crop-btn {
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    color: white;
    cursor: pointer;
    font-size: 16px;
}

.crop-confirm {
    background-color: var(--myblue);
}

.crop-cancel {
    background-color: var(--mygreey2);
}
</style>
